import re
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import SparkApi

def extract_news_data(page_source):
    """
    从网页源码中提取新闻数据。
    """
    # 定义正则表达式模式，用于匹配新闻的标题、链接、日期和摘要
    patterns = {
        'title': r'<div class="news_item_t".*?><a href=".*?" target="_blank">(.*?)</a></div>',
        'href': r'<div class="news_item_t".*?><a href="(.*?)" target="_blank">.*?</a></div>',
        'date': r'<span class="news_item_time">(\d+-\d+-\d+ \d+:\d+:\d+) - </span>',
        'abstract': r'<div class="news_item_c"><span class="news_item_time">.*? - </span><span>(.*?)</span></div>',
    }
    
    # 使用正则表达式提取新闻数据
    # 这里遍历patterns字典，对每个正则表达式模式进行查找，并将结果存储在news_data字典中
    news_data = {
        key: re.findall(pattern, page_source) for key, pattern in patterns.items()
    }
    
    # 找出所有字段中匹配到的最少的数量，确保每个新闻条目字段都是完整的
    min_len = min(len(data) for data in news_data.values())
    
    # 清理每个字段的HTML标签，并组装成最终的新闻数据列表
    # 这个步骤创建一个列表，其中每个元素是一个字典，表示一个新闻条目
    # 在这个过程中，我们用re.sub函数去除所有HTML标签
    cleaned_data = [
        {key: re.sub('<.*?>', '', news_data[key][i]) for key in patterns.keys()}
        for i in range(min_len)
    ]
    
    return cleaned_data


def get_company_news(stock_code, pages=None):
    """获取公司新闻"""
    edge_options = webdriver.EdgeOptions()  
    edge_options.add_argument('--headless')  # 无界面模式，如果需要调试，可以注释掉这行
    
    with webdriver.Edge(options=edge_options) as browser:
        news_url = f'https://so.eastmoney.com/news/s?keyword={stock_code}'
        browser.get(news_url)
        time.sleep(2)
        all_news_data = []
        for _ in range(pages or float('inf')):
            data = browser.page_source
            news_list = extract_news_data(data)
            if not news_list: break
            all_news_data.extend(news_list)
            try:
                next_page_button = browser.find_element(By.CSS_SELECTOR, "a[title='下一页']")
                if next_page_button: next_page_button.click()
                else: break
                time.sleep(2)
            except Exception: break
        return pd.DataFrame(all_news_data)


def spark_api_sentiment_score(text, max_retries=3):
    """调用讯飞星火API来评分新闻情感的函数，确保返回一个有效的评分结果。
    如果结果不是有效的数字，将重试直到获取有效评分或达到最大重试次数。
    """
    appid = "76ad8ad6" #填写你的id
    api_secret = "MThlNmI0MDNlOTlmMDFmNjVlNDM1NWIz" #填写你的secret
    api_key = "6cb00aa86ff27d2918fb55e88f466298"  #填写你的api_key
    domain = "lite"
    Spark_url = "wss://spark-api.xf-yun.com/v1.1/chat"
    
    for attempt in range(max_retries):
        character = {"role": "system", "content": "你现在是财经新闻打分系统，擅长分析新闻对股价的评分，（从极度负面到极度正面，-1到1分），仅输出一个数字(保留小数点后3位)，不要输出其他文本内容"}
        news = {"role": "user", "content": '仅输出一个数字(保留小数点后3位)，不要输出其他文本内容' + text}
        question = [character, news]
        SparkApi.answer = ''
        SparkApi.main(appid, api_key, api_secret, Spark_url, domain, question)
        
        try:
            score = float(SparkApi.answer)
            return score
        except ValueError:
            print(f"尝试 #{attempt+1}: 无法从API响应中提取有效评分: {SparkApi.answer}")
            continue  # 重试直到获取到有效评分或达到最大重试次数

    # 如果所有重试后仍未获取到有效评分，可以抛出异常或者处理这种情况
    raise ValueError(f"在{max_retries}次重试后，仍无法获取有效的评分。")

def sentiment_analysis(news_df):
    """情感分析和计算每天的平均评分。"""
    news_df['score'] = news_df['title'].apply(spark_api_sentiment_score)  # 假设简化为直接调用
    news_df['date'] = pd.to_datetime(news_df['date']).dt.date
    daily_average_scores = news_df.groupby('date')['score'].mean().reset_index()
    return news_df, daily_average_scores

# 示例用法简化，直接显示结果
news_df = get_company_news('阿里巴巴', 2)
print(news_df) #用于调试
all_scores, daily_scores = sentiment_analysis(news_df)
print(all_scores, daily_scores)
# 将所有新闻数据及其评分保存到Excel文件
all_scores.to_excel('all_scores.xlsx', index=False)
# 将每天的平均评分保存到Excel文件
daily_scores.to_excel('daily_scores.xlsx', index=False)